package com.bosicloud.cmb.ibp.edge.controller;


import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.amazonaws.regions.Regions;
import com.amazonaws.services.ec2.model.InstanceType;
import com.bosicloud.cmb.ibp.account.common.model.IbpUser;
import com.bosicloud.cmb.ibp.aws.adapter.api.CloudFormationClient;
import com.bosicloud.cmb.ibp.commom.dto.UnionResult;
import com.bosicloud.cmb.ibp.commom.exception.ServiceException;
import com.bosicloud.cmb.ibp.commom.utils.JsonUtil;
import com.bosicloud.cmb.ibp.edge.config.RedisLock;
import com.bosicloud.cmb.ibp.edge.constant.SessionKeys;
import com.bosicloud.cmb.ibp.edge.constant.UrlDefine;
import com.bosicloud.cmb.ibp.microplatform.api.MicroPlatformManagerClient;
import com.bosicloud.cmb.ibp.microplatform.commom.constant.CloudFormationConstant;
import com.bosicloud.cmb.ibp.microplatform.commom.constant.ResultConstant;
import com.bosicloud.cmb.ibp.microplatform.commom.model.IbpMspProjectInfo;
import com.bosicloud.cmb.ibp.microplatform.commom.vo.CreateMicroPlatformVO;
import com.bosicloud.cmb.ibp.microplatform.commom.vo.DeployServiceVO;
import com.bosicloud.cmb.ibp.microplatform.commom.vo.ProjectInfoVO;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import javax.validation.Valid;
import java.util.Objects;
import java.util.Optional;


@Api(value = "微服务平台Controller", description = "微服务平台Controller")
@RestController
@Slf4j
public class MicroPlatformController {

    @Autowired
    private MicroPlatformManagerClient platformManagerClient;

    @Autowired
    private CloudFormationClient formationClient;

    @Autowired
    private RedisLock lock;

    @ApiOperation(value = "自动化创建微服务平台", response = UnionResult.class)
    @HystrixCommand
    @PostMapping(value = UrlDefine.CREATE_MICRO_SERVICE_PLATFORM, produces = MediaType.APPLICATION_JSON_VALUE)
    public Object createMicroServicePlatform(@ApiParam(name = "paramVO", value = "ibpApiBean实体", required = true)
                                             @RequestBody @Valid CreateMicroPlatformVO paramVO,
                                             HttpServletRequest request) {
        log.info("### CreateMicroServicePlatform Params:{}", JsonUtil.toJson(paramVO));
        Optional.of(paramVO)
                .orElseThrow(() -> new ServiceException("创建微服务参数不能为空.",
                        UnionResult.StatusEnum.ParamsFail.code()));

        if (Objects.isNull(paramVO.getCreateUser()) || paramVO.getCreateUser() < 1) {
            paramVO.setCreateUser(getUserFromSession(request).getId());
        }
        Optional<IbpMspProjectInfo> optionalInfo = Optional.ofNullable(platformManagerClient
                .searchProjectInfo(paramVO.getCreateUser()));
        if (!optionalInfo.isPresent()) {
            throw new ServiceException("该用户还未添加项目", UnionResult.StatusEnum.Fail.code());
        }

        Optional.of(platformManagerClient.checkClusterNameIsExits(paramVO.getEnvName(), paramVO.getClusterName()))
                .filter(b -> !Objects.equals(b, true))
                .orElseThrow(() -> new ServiceException("当前环境,集群名已经存在.", UnionResult.StatusEnum.Fail.code()));

        try {
            InstanceType.fromValue(paramVO.getEc2Type().toLowerCase());
        } catch (IllegalArgumentException e) {
            log.error(e.getMessage(), e);
            throw new ServiceException(String.format("Ec2Type参数错误, 未发现此实例类型 : %s", paramVO.getEc2Type()),
                    UnionResult.StatusEnum.ParamsFail.code());
        }
        try {
            Regions.fromName(paramVO.getRegionName().trim().toLowerCase());
        } catch (IllegalArgumentException e) {
            log.error(e.getMessage(), e);
            throw new ServiceException(String.format("Region参数错误, 未发现Region : %s", paramVO.getRegionName()),
                    UnionResult.StatusEnum.ParamsFail.code());
        }

        String token = paramVO.getToken();
        try {
            if (lock.lock(token)) {
                if (Objects.nonNull(platformManagerClient.searchMspEnvironmentByToken(token))) {
                    throw new ServiceException("当前微服务已创建过，请不要重复创建.", UnionResult.StatusEnum.ParamsFail.code());
                }
                platformManagerClient.createMicroServicePlatform(paramVO);
            }
        } catch (ServiceException e) {
            log.error("创建微服务失败.message:{}", e.getMessage(), e);
            throw e;
        } catch (Exception e) {
            log.error("创建微服务失败.message:{}", e.getMessage(), e);
            throw e;
        } finally {
            lock.unlock(token);
        }
        return ResultConstant.SUCCESS;
    }


    @ApiOperation(value = "查询微服务环境列表", response = UnionResult.class)
    @HystrixCommand
    @GetMapping(value = UrlDefine.SEARCH_ENV_LIST + "/{userId}", produces = MediaType.APPLICATION_JSON_VALUE)
    public Object searchEnvList(
            @ApiParam(name = "userId", value = "用户ID", required = true)
            @PathVariable(value = "userId") Integer userId) {
        return platformManagerClient.searchEnvList(userId);
    }

    @ApiOperation(value = "根据平台环境ID/环境名称查询平台组件列表", response = UnionResult.class)
    @HystrixCommand
    @GetMapping(value = UrlDefine.SEARCH_PLATFORM_COMPONENT_LIST + "/{environmentId}/{envName}", produces = MediaType.APPLICATION_JSON_VALUE)
    public Object searchPlatformComponentList(
            @ApiParam(name = "environmentId", value = "平台环境ID", required = true)
            @PathVariable(value = "environmentId") Integer environmentId,
            @ApiParam(name = "envName", value = "环境", required = true)
            @PathVariable(value = "envName") String envName) {
        return platformManagerClient.searchPlatformComponentList(environmentId, envName);
    }


    @ApiOperation(value = "根据Token查询事件日志", response = UnionResult.class)
    @HystrixCommand
    @GetMapping(value = UrlDefine.SEARCH_CF_NOTIFY_MESSAGE + "/{token}", produces = MediaType.APPLICATION_JSON_VALUE)
    public Object searchCfNotifyMessage(@ApiParam(name = "token", value = "创建微服务平台时的token", required = true)
                                        @PathVariable(value = "token") String token) {
        return platformManagerClient.searchCfNotifyMessage(token);
    }

    @ApiOperation(value = "EC2 Instace Type", response = UnionResult.class)
    @HystrixCommand
    @GetMapping(value = UrlDefine.GET_EC2_INSTANCE_TYPE, produces = MediaType.APPLICATION_JSON_VALUE)
    public Object getEc2InstaceType() {

        JSONArray array = new JSONArray();
        for (InstanceType type : InstanceType.values()) {
            JSONObject json = new JSONObject();
            json.put("name", type.name());
            json.put("code", type.toString());
            array.add(json);
        }

        return array;
    }

    @ApiOperation(value = "获取创建AWS基础环境Token", response = UnionResult.class)
    @HystrixCommand
    @GetMapping(value = UrlDefine.CF_CREATE_TOKEN, produces = MediaType.APPLICATION_JSON_VALUE)
    public Object getCreateCFStackToken() {
        return formationClient.getCreateCFStackToken();
    }

    private IbpUser getUserFromSession(HttpServletRequest request) {
        Object obj = request.getSession().getAttribute(SessionKeys.ACCOUNT);
        if (Objects.isNull(obj)) {
            throw new ServiceException("未找到会话信息.", UnionResult.StatusEnum.FailToken.code());
        }
        return (IbpUser) obj;

    }

    @ApiOperation(value = "部署微服务", response = UnionResult.class)
    @HystrixCommand
    @PostMapping(value = UrlDefine.DEPLOY_SERVICE, produces = MediaType.APPLICATION_JSON_VALUE)
    public Object deployService(@RequestBody @Valid DeployServiceVO deployVO,
                                HttpServletRequest request) {
        log.info("### Deploy App Service Params:{}", JsonUtil.toJson(deployVO));
        Optional.of(deployVO)
                .orElseThrow(() -> new ServiceException("部署微服务参数不能为空.",
                        UnionResult.StatusEnum.ParamsFail.code()));

        Optional.of(platformManagerClient.checkClusterNameIsExits(deployVO.getEnvName(), deployVO.getClusterName()))
                .filter(b -> Objects.equals(b, true))
                .orElseThrow(() -> new ServiceException("当前环境,集群名不存在.",
                        UnionResult.StatusEnum.ParamsFail.code()));

        Optional.of(platformManagerClient.searchMspEnvironmentInfo(deployVO.getEnvName(), deployVO.getClusterName()))
                .filter(rs -> Objects.equals(rs.getStatus(), CloudFormationConstant.COMPLETE_STATUS))
                .orElseThrow(() -> new ServiceException("当前微服务平台未创建成功.", UnionResult.StatusEnum.Fail.code()));


        if (Objects.nonNull(platformManagerClient.searchDeployServiceInfo(deployVO.getEnvName(),
                deployVO.getClusterName(), deployVO.getServiceType(), deployVO.getServiceName()))) {
            throw new ServiceException("当前环境下不支持重复部署同一个应用.", UnionResult.StatusEnum.ParamsFail.code());
        }
        try {
            if (Objects.isNull(deployVO.getCreateUser()) || deployVO.getCreateUser() < 1) {
                deployVO.setCreateUser(getUserFromSession(request).getId());
            }
            return platformManagerClient.deployService(deployVO);
        } catch (Exception e) {
            log.error("部署微服务异常,异常信息:{}", e.getMessage(), e);
            throw e;
        }
    }

    @ApiOperation(value = "根据微服务环境查询集群名称列表", response = UnionResult.class)
    @HystrixCommand
    @GetMapping(value = UrlDefine.SEARCH_CLUSTER_NAME_LIST + "/{envName}", produces = MediaType.APPLICATION_JSON_VALUE)
    public Object searchClusterNameList(@ApiParam(name = "envName", value = "环境名称", required = true)
                                        @PathVariable(value = "envName") String envName,
                                        HttpServletRequest request) {
        return platformManagerClient.searchClusterNameList(getUserFromSession(request).getId(), envName);
    }


    @ApiOperation(value = "查询微服务列表", response = UnionResult.class)
    @HystrixCommand
    @GetMapping(value = UrlDefine.SEARCH_SERVICE_LIST + "/{environmentId}", produces = MediaType.APPLICATION_JSON_VALUE)
    public Object searchServiceList(@ApiParam(name = "environmentId", value = "环境ID", required = true)
                                    @PathVariable(value = "environmentId") Integer environmentId) {

        return platformManagerClient.searchServiceList(environmentId);

    }


    @ApiOperation(value = "根据部署ID,任务名称查询部署错误信息", response = UnionResult.class)
    @HystrixCommand
    @GetMapping(value = UrlDefine.SEARCH_FAIL_MESSAGE + "/{deployId}/{taskName}",
            produces = MediaType.APPLICATION_JSON_VALUE)
    public Object searchFailMessage(@ApiParam(name = "deployId", value = "部署ID", required = true)
                                    @PathVariable(value = "deployId") Integer deployId,
                                    @ApiParam(name = "taskName", value = "任务名称", required = true)
                                    @PathVariable(value = "taskName") String taskName) {

        return platformManagerClient.searchFailMessage(deployId, taskName);

    }


    @ApiOperation(value = "根据环境和集群名查询微服务环境信息", response = UnionResult.class)
    @HystrixCommand
    @GetMapping(value = UrlDefine.SEARCH_MSPENVIRONMENT_INFO + "/{envName}/{clusterName}",
            produces = MediaType.APPLICATION_JSON_VALUE)
    public Object searchMspEnvironmentInfo(@ApiParam(name = "envName", value = "envName", required = true)
                                           @PathVariable(value = "envName") String envName,
                                           @ApiParam(name = "clusterName", value = "clusterName", required = true)
                                           @PathVariable(value = "clusterName") String clusterName) {
        return platformManagerClient.searchMspEnvironmentInfo(envName, clusterName);

    }

    @ApiOperation(value = "添加项目信息", response = UnionResult.class)
    @HystrixCommand
    @PostMapping(value = UrlDefine.ADD_PROJECT_INFO, produces = MediaType.APPLICATION_JSON_VALUE)
    public Object saveProjectInfo(@ApiParam(name = "projectInfoVO", value = "projectInfoVO", required = true)
                                  @RequestBody @Valid ProjectInfoVO projectInfoVO,
                                  HttpServletRequest request) {
        if (Objects.isNull(projectInfoVO.getCreateUser())  || projectInfoVO.getCreateUser()<1) {
            projectInfoVO.setCreateUser(getUserFromSession(request).getId());
        }
        Optional<IbpMspProjectInfo> optionalInfo = Optional.ofNullable(platformManagerClient
                .searchProjectInfo(projectInfoVO.getCreateUser()));
        if (optionalInfo.isPresent()) {
            throw new ServiceException("该用户已经已添加过项目信息,请不要重复添加.", UnionResult.StatusEnum.Fail.code());
        }
        return platformManagerClient.saveProjectInfo(projectInfoVO);
    }

    @ApiOperation(value = "检查用户项目是否已经存在", response = UnionResult.class)
    @HystrixCommand
    @GetMapping(value = UrlDefine.SEARCH_PROJECT_INFO + "/{userId}", produces = MediaType.APPLICATION_JSON_VALUE)
    public Object checkProjectIsExist(@ApiParam(name = "userId", value = "登录用户ID", required = true)
                                      @PathVariable(value = "userId") Integer userId) {
        return Optional.ofNullable(platformManagerClient.searchProjectInfo(userId))
                .isPresent();
    }
}
